package lenfs.impl;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;

import lenfs.Server;
import lenfs.util.FileDescriptor;
import lenfs.util.FileHandle;
import lenfs.util.XDR;
import lenfs.util.constants.CommonConst;
import lenfs.util.constants.NFSConst;
import lenfs.util.constants.RPCConst;

public class NFSImpl extends ServerImpl {

	public NFSImpl(Server server) {
		super(server);
	}

	private static void addAttribute(XDR xdrPacket,
			FileDescriptor fileDescriptor, FileHandle fileHandle) {
		// initially
		int objectType = NFSConst.NFNON;
		int objectMode = NFSConst.MODE_NFNON;
		long size = 0;
		int links = 0;
		int blocksize = CommonConst.BLOCKSIZE;

		fileDescriptor.readLock();

		File file = fileDescriptor.getFile();

		// define type
		if (file.isFile()) {
			objectType = NFSConst.NFREG;
			objectMode = NFSConst.MODE_NFREG;
			size = file.length();
			links = 1;
		}

		else if (file.isDirectory()) {
			objectType = NFSConst.NFDIR;
			objectMode = NFSConst.MODE_NFDIR;
			size = 0;
			links = 2;
		}

		else {
			links = 3;
		}

		// define mode
		if (file.canRead())
			objectMode |= NFSConst.PERM_OR;

		if (file.canWrite())
			objectMode |= NFSConst.PERM_OW;

		if (file.canExecute())
			objectMode |= NFSConst.PERM_OE;

		long lastModificationTime = file.lastModified();

		fileDescriptor.readUnlock();

		int uid = -1;
		int gid = -1;
		int rdev = -1;
		long blocks = (size + blocksize - 1) / blocksize;

		// fill xdrPacket
		xdrPacket.addUInt(objectType);
		xdrPacket.addUInt(objectMode);
		xdrPacket.addUInt(links);
		xdrPacket.addUInt(uid);
		xdrPacket.addUInt(gid);
		xdrPacket.addUInt(size);
		xdrPacket.addUInt(blocksize);
		xdrPacket.addUInt(rdev);
		xdrPacket.addUInt(blocks);
		fileHandle.infToPacket(xdrPacket);

		attachTimestamp(xdrPacket, lastModificationTime);
		attachTimestamp(xdrPacket, 0);
		attachTimestamp(xdrPacket, lastModificationTime);
	}

	private void getAttribute(XDR xdrPacket, long xid) {
		XDR reply = null;

		FileHandle handle = FileHandle.readFromPacket(xdrPacket);
		if (handle == null) {
			voidError(xdrPacket, xid, RPCConst.GARBAGE_ARGS);
			return;
		}
		FileDescriptor fileDesc = server.getFile(handle);
		if (fileDesc == null) {
			reply = successAnswer(xdrPacket, 0, xid, NFSConst.NFSERR_STALE);
			reply.send();
			return;
		}
		reply = successAnswer(xdrPacket, NFSConst.FATTR_SIZE, xid,
				NFSConst.NFS_OK);
		addAttribute(reply, fileDesc, handle);
		reply.send();
	}

	private void statFS(XDR xdrPacket, long xid) {
		XDR xdrReply = null;

		FileHandle fileHandle = FileHandle.readFromPacket(xdrPacket);
		if (fileHandle == null) {
			voidError(xdrPacket, xid, RPCConst.GARBAGE_ARGS);
			return;
		}

		FileDescriptor fileDesc = server.getFile(fileHandle);

		if (fileDesc == null) {
			xdrReply = successAnswer(xdrPacket, 0, xid, NFSConst.NFSERR_STALE);
			xdrReply.send();
			return;
		}

		File file = fileDesc.getFile();
		xdrReply = successAnswer(xdrPacket, 20, xid, NFSConst.NFS_OK);

		File root = server.getRootFile(file);
		xdrReply.addUInt(NFSConst.MAXDATA);

		int blockSize = CommonConst.BLOCKSIZE;
		xdrReply.addUInt(CommonConst.BLOCKSIZE);
		xdrReply.addUInt(root.getTotalSpace() / blockSize);
		xdrReply.addUInt(root.getFreeSpace() / blockSize);
		xdrReply.addUInt(root.getUsableSpace() / blockSize);

		xdrReply.send();
	}

	private void lookup(XDR xdrPacket, long xid) {
		XDR xdrReply = null;

		FileHandle fileHandle = FileHandle.readFromPacket(xdrPacket);
		if (fileHandle == null) {
			voidError(xdrPacket, xid, RPCConst.GARBAGE_ARGS);
			return;
		}

		FileDescriptor rootDesc = server.getFile(fileHandle);
		if (rootDesc == null) {
			xdrReply = successAnswer(xdrPacket, 0, xid, NFSConst.NFSERR_STALE);
			xdrReply.send();
			return;
		}

		File root = rootDesc.getFile();

		// as in mounting
		long packetLenght = xdrPacket.getUInt();

		if (packetLenght < 0) {
			voidError(xdrPacket, xid, RPCConst.GARBAGE_ARGS);
			return;
		}

		if (packetLenght <= 0) {
			xdrReply = successAnswer(xdrPacket, 0, xid, NFSConst.NFSERR_NOENT);
			xdrReply.send();
			return;
		}

		else if (packetLenght > NFSConst.MAXNAMELEN) {
			xdrReply = successAnswer(xdrPacket, 0, xid,
					NFSConst.NFSERR_NAMETOOLONG);
			xdrReply.send();
			return;
		}

		String filename = xdrPacket.getString(packetLenght);

		xdrReply = successAnswer(xdrPacket, NFSConst.FATTR_SIZE, xid,
				NFSConst.NFS_OK);

		File file = new File(root, filename).getAbsoluteFile();
		if (!file.exists()) {
			xdrReply = successAnswer(xdrPacket, 0, xid, NFSConst.NFSERR_NOENT);
			xdrReply.send();
			return;
		}

		FileHandle newFileHandle = server.generateFileHandle(file, fileHandle);

		if (newFileHandle == null) {
			xdrReply = successAnswer(xdrPacket, 0, xid, NFSConst.NFSERR_NOSPC);
			xdrReply.send();
			return;
		}

		FileDescriptor fileDescriptor = server.getFile(newFileHandle);

		xdrReply = successAnswer(xdrPacket, NFSConst.FATTR_SIZE
				+ NFSConst.FHSIZE, xid, NFSConst.NFS_OK);
		newFileHandle.toPacket(xdrReply);
		addAttribute(xdrReply, fileDescriptor, newFileHandle);

		xdrReply.send();
	}

	private void readDirectory(XDR xdrPacket, long xid) {
		XDR xdrReply = null;

		FileHandle fileHandle = FileHandle.readFromPacket(xdrPacket);

		if (fileHandle == null) {
			voidError(xdrPacket, xid, RPCConst.GARBAGE_ARGS);
			return;
		}

		long initCookie = xdrPacket.getCookie();
		int additionalLenght = (int) xdrPacket.getUInt() - HEADER_LENGTH - 1;

		FileDescriptor pathDesc = server.getFile(fileHandle);
		if (pathDesc == null) {
			xdrReply = successAnswer(xdrPacket, 0, xid, NFSConst.NFSERR_STALE);
			xdrReply.send();
			return;
		}

		File path = pathDesc.getFile();

		if (!path.isDirectory()) {
			xdrReply = successAnswer(xdrPacket, 0, xid, NFSConst.NFSERR_NOTDIR);
			xdrReply.send();
			return;
		}

		pathDesc.readLock();
		File[] dirContent = path.listFiles();

		long eventCookie = 0;

		xdrReply = successAnswer(xdrPacket, additionalLenght + 1, xid,
				NFSConst.NFS_OK);
		long eventSize = initCookie + additionalLenght;

		int eof = 1;

		for (File entry : dirContent) {
			String name = entry.getName();
			int fileSize = XDR.UNIT + name.length() + NFSConst.COOKIESIZE;
			if (eventCookie >= initCookie) {
				if (eventCookie + fileSize > eventSize) {
					eof = 0;
					break;
				}
				xdrReply.addUInt(1);
				server.generateFileHandle(entry, fileHandle);
				xdrReply.addUInt(entry.getAbsoluteFile().hashCode());
				xdrReply.addString(name);
				xdrReply.addUInt(eventCookie + fileSize);
			}
			eventCookie += fileSize;
		}

		pathDesc.readUnlock();
		xdrReply.addUInt(0);
		xdrReply.addUInt(eof);
		xdrReply.send();
	}

	private void makeDirectory(XDR xdrPacket, long xid) {

		XDR xdrReply = null;
		FileHandle handle = FileHandle.readFromPacket(xdrPacket);

		if (handle == null) {
			voidError(xdrPacket, xid, RPCConst.GARBAGE_ARGS);
			return;
		}

		FileDescriptor pathDesc = server.getFile(handle);
		if (pathDesc == null) {
			xdrReply = successAnswer(xdrPacket, 0, xid, NFSConst.NFSERR_STALE);
			xdrReply.send();
			return;
		}

		File path = pathDesc.getFile();

		long length = xdrPacket.getUInt();
		if (length <= 0) {
			voidError(xdrPacket, xid, RPCConst.GARBAGE_ARGS);
			return;
		} else if (length > NFSConst.MAXNAMELEN) {
			xdrReply = successAnswer(xdrPacket, 0, xid,
					NFSConst.NFSERR_NAMETOOLONG);
			xdrReply.send();
			return;
		}
		File newDir = new File(path, xdrPacket.getString(length));

		int mode = xdrPacket.getInt();

		if (mode != -1 && (mode & NFSConst.MODE_NFDIR) != NFSConst.MODE_NFDIR) {
			voidError(xdrPacket, xid, RPCConst.GARBAGE_ARGS);
			return;
		}

		xdrPacket.getUInt();
		xdrPacket.getUInt();
		xdrPacket.getUInt();

		if (newDir.exists()) {
			xdrReply = successAnswer(xdrPacket, 0, xid, NFSConst.NFSERR_EXIST);
			xdrReply.send();
			return;
		}

		boolean ret = newDir.mkdir();

		if (ret == false) {
			xdrReply = successAnswer(xdrPacket, 0, xid, NFSConst.NFSERR_IO);
			xdrReply.send();
			return;
		}

		boolean rperm = (mode & NFSConst.PERM_OR) == NFSConst.PERM_OR;
		boolean wperm = (mode & NFSConst.PERM_OW) == NFSConst.PERM_OW;
		boolean eperm = (mode & NFSConst.PERM_OE) == NFSConst.PERM_OE;

		// timestamps
		xdrPacket.getUInt();
		xdrPacket.getUInt();

		long msec = xdrPacket.getUInt();
		long musec = xdrPacket.getUInt();

		long mtime = msec * 1000 + musec / 1000;

		newDir.setReadable(rperm);
		newDir.setWritable(wperm);
		newDir.setExecutable(eperm);

		newDir.setLastModified(mtime);

		FileHandle newDirHandle = server.generateFileHandle(newDir, handle);
		FileDescriptor newDirDesc = server.getFile(newDirHandle);

		if (newDirDesc == null) {
			voidError(xdrPacket, xid, RPCConst.SYSTEM_ERR);
			return;
		}

		xdrReply = successAnswer(xdrPacket, NFSConst.FATTR_SIZE
				+ NFSConst.FHSIZE, xid, NFSConst.NFS_OK);
		newDirHandle.toPacket(xdrReply);

		addAttribute(xdrReply, newDirDesc, newDirHandle);
		xdrReply.send();
	}

	private void removeDirectory(XDR xdrPacket, long xid) {
		FileHandle handle = FileHandle.readFromPacket(xdrPacket);
		XDR xdrReply = null;
		if (handle == null) {
			voidError(xdrPacket, xid, RPCConst.GARBAGE_ARGS);
			return;
		}

		FileDescriptor pathDesc = server.getFile(handle);
		if (pathDesc == null) {
			xdrReply = successAnswer(xdrPacket, 0, xid, NFSConst.NFSERR_STALE);
			xdrReply.send();
			return;
		}

		File path = pathDesc.getFile();

		long length = xdrPacket.getUInt();
		if (length <= 0) {
			voidError(xdrPacket, xid, RPCConst.GARBAGE_ARGS);
			return;
		} else if (length > NFSConst.MAXNAMELEN) {
			xdrReply = successAnswer(xdrPacket, 0, xid,
					NFSConst.NFSERR_NAMETOOLONG);
			xdrReply.send();
			return;
		}
		String name = xdrPacket.getString(length);

		File dirToBeRemoved = new File(path, name);

		if (!dirToBeRemoved.exists()) {
			xdrReply = successAnswer(xdrPacket, 0, xid, NFSConst.NFSERR_NOENT);
			xdrReply.send();
			return;
		} else if (!dirToBeRemoved.isDirectory()) {
			xdrReply = successAnswer(xdrPacket, 0, xid, NFSConst.NFSERR_NOTDIR);
			xdrReply.send();
			return;
		}

		boolean res = dirToBeRemoved.delete();

		if (res == false) {
			xdrReply = successAnswer(xdrPacket, 0, xid, NFSConst.NFSERR_IO);
			xdrReply.send();
			return;
		}

		server.deleteEntry(dirToBeRemoved);
		xdrReply = successAnswer(xdrPacket, 0, xid, NFSConst.NFS_OK);
		xdrReply.send();
	}

	public void create(XDR xdrPacket, long xid) {
		FileHandle handle = FileHandle.readFromPacket(xdrPacket);
		XDR xdrReply = null;
		if (handle == null) {
			voidError(xdrPacket, xid, RPCConst.GARBAGE_ARGS);
			return;
		}

		FileDescriptor pathDesc = server.getFile(handle);
		if (pathDesc == null) {
			xdrReply = successAnswer(xdrPacket, 0, xid, NFSConst.NFSERR_STALE);
			xdrReply.send();
			return;
		}

		File path = pathDesc.getFile();
		long length = xdrPacket.getUInt();
		File newFile = new File(path, xdrPacket.getString(length));

		int mode = xdrPacket.getInt();

		xdrPacket.getUInt();
		xdrPacket.getUInt();
		xdrPacket.getUInt();
		xdrPacket.getUInt();
		xdrPacket.getUInt();

		long sec = xdrPacket.getUInt();
		long usec = xdrPacket.getUInt();

		boolean ret = false;
		try {
			ret = newFile.createNewFile();
		} catch (IOException e) {
			e.printStackTrace();
		}

		if (ret == false) {
			xdrReply = successAnswer(xdrPacket, 0, xid, NFSConst.NFSERR_EXIST);
			xdrReply.send();
			return;
		}

		boolean rperm = (mode & NFSConst.PERM_OR) == NFSConst.PERM_OR;
		boolean wperm = (mode & NFSConst.PERM_OW) == NFSConst.PERM_OW;
		boolean eperm = (mode & NFSConst.PERM_OE) == NFSConst.PERM_OE;

		newFile.setReadable(rperm);
		newFile.setWritable(wperm);
		newFile.setExecutable(eperm);

		long time = sec * 1000 + usec / 1000;
		newFile.setLastModified(time);

		FileHandle newFileHandle = server.generateFileHandle(newFile, handle);
		FileDescriptor newFileDesc = server.getFile(newFileHandle);
		if (newFileDesc == null) {
			voidError(xdrPacket, xid, RPCConst.SYSTEM_ERR);
			return;
		}

		xdrReply = successAnswer(xdrPacket, NFSConst.FATTR_SIZE
				+ NFSConst.FHSIZE, xid, NFSConst.NFS_OK);
		newFileHandle.toPacket(xdrReply);

		addAttribute(xdrReply, newFileDesc, newFileHandle);
		xdrReply.send();
	}

	private void nfsprocRemove(XDR packet, long xid) {
		FileHandle handle = FileHandle.readFromPacket(packet);
		XDR xdrReply = null;
		if (handle == null) {
			voidError(packet, xid, RPCConst.GARBAGE_ARGS);
			return;
		}

		FileDescriptor pathDesc = server.getFile(handle);
		if (pathDesc == null) {
			xdrReply = successAnswer(packet, 0, xid, NFSConst.NFSERR_STALE);
			xdrReply.send();
			return;
		}

		File path = pathDesc.getFile();

		long length = packet.getUInt();
		String name = packet.getString(length);

		File fileToBeRemoved = new File(path, name);

		if (!fileToBeRemoved.exists()) {
			xdrReply = successAnswer(packet, 0, xid, NFSConst.NFSERR_NOENT);
			xdrReply.send();
			return;
		}

		boolean result = fileToBeRemoved.delete();

		if (result == false) {
			xdrReply = successAnswer(packet, 0, xid, NFSConst.NFSERR_IO);
			xdrReply.send();
			return;
		}

		server.deleteEntry(fileToBeRemoved);
		xdrReply = successAnswer(packet, 0, xid, NFSConst.NFS_OK);
		xdrReply.send();
	}

	private void rename(XDR xdrPacket, long xid) {
		FileHandle handle = FileHandle.readFromPacket(xdrPacket);
		XDR xdrReply = null;
		if (handle == null) {
			voidError(xdrPacket, xid, RPCConst.GARBAGE_ARGS);
			return;
		}

		FileDescriptor pathDesc = server.getFile(handle);
		File path = pathDesc.getFile();

		long length = xdrPacket.getUInt();
		String name = xdrPacket.getString(length);

		File file = new File(path, name);

		if (!file.exists()) {
			xdrReply = successAnswer(xdrPacket, 0, xid, NFSConst.NFSERR_NOENT);
			xdrReply.send();
			return;
		}

		FileHandle toHandle = FileHandle.readFromPacket(xdrPacket);

		if (toHandle == null) {
			xdrReply = successAnswer(xdrPacket, 0, xid, NFSConst.NFSERR_STALE);
			xdrReply.send();
			return;
		}

		FileDescriptor toPathDesc = server.getFile(handle);

		if (toPathDesc == null) {
			xdrReply = successAnswer(xdrPacket, 0, xid, NFSConst.NFSERR_STALE);
			xdrReply.send();
			return;
		}

		File toPath = toPathDesc.getFile();
		long toLength = xdrPacket.getUInt();
		String toName = xdrPacket.getString(toLength);

		File renamedFile = new File(toPath, toName);
		boolean renExists = renamedFile.exists();
		boolean res = file.renameTo(renamedFile);

		if (res == false) {
			xdrReply = successAnswer(xdrPacket, 0, xid, NFSConst.NFSERR_IO);
			xdrReply.send();
			return;
		} else if (renExists)
			server.deleteEntry(renamedFile);

		server.rebindEntry(renamedFile, file);

		xdrReply = successAnswer(xdrPacket, 0, xid, NFSConst.NFS_OK);
		xdrReply.send();
	}

	private void read(XDR xdrPacket, long xid) {
		FileHandle handle = FileHandle.readFromPacket(xdrPacket);
		XDR xdrReply = null;
		if (handle == null) {
			voidError(xdrPacket, xid, RPCConst.GARBAGE_ARGS);
			return;
		}

		FileDescriptor fileDesc = server.getFile(handle);
		File file = fileDesc.getFile();

		if (file.isDirectory()) {
			xdrReply = successAnswer(xdrPacket, 0, xid, NFSConst.NFSERR_ISDIR);
			xdrReply.send();
			return;
		}

		int offset = (int) xdrPacket.getUInt();
		int count = (int) xdrPacket.getUInt();
		xdrPacket.getUInt();

		byte[] byteArray = null;
		int ret = -1;
		fileDesc.readLock();
		try {
			FileInputStream f = new FileInputStream(file);
			FileChannel ch = f.getChannel();
			MappedByteBuffer mb = ch.map(FileChannel.MapMode.READ_ONLY, offset,
					ch.size() - offset);
			ret = Math.min(mb.remaining(), count);
			byteArray = new byte[ret];
			mb.get(byteArray, 0, ret);
			f.close();
		} catch (Exception e) {
			e.printStackTrace();
			fileDesc.readUnlock();
			xdrReply = successAnswer(xdrPacket, 0, xid, NFSConst.NFSERR_IO);
			xdrReply.send();
			return;
		}
		fileDesc.readUnlock();
		xdrReply = successAnswer(xdrPacket, XDR.UNIT + ret
				+ NFSConst.FATTR_SIZE, xid, NFSConst.NFS_OK);
		addAttribute(xdrReply, fileDesc, handle);
		xdrReply.addBytes(byteArray, ret);
		xdrReply.send();
	}

	private void write(XDR xdrPacket, long xid) {
		FileHandle handle = FileHandle.readFromPacket(xdrPacket);
		XDR xdrReply = null;
		if (handle == null) {
			voidError(xdrPacket, xid, RPCConst.GARBAGE_ARGS);
			return;
		}

		FileDescriptor fileDesc = server.getFile(handle);

		File file = fileDesc.getFile();

		if (file.isDirectory()) {
			xdrReply = successAnswer(xdrPacket, 0, xid, NFSConst.NFSERR_ISDIR);
			xdrReply.send();
			return;
		}

		xdrPacket.getUInt();
		int offset = (int) xdrPacket.getUInt();
		xdrPacket.getUInt();

		byte[] byteArray = xdrPacket.getBytes();

		if (offset == -1 || byteArray == null) {
			voidError(xdrPacket, xid, RPCConst.GARBAGE_ARGS);
			return;
		}

		fileDesc.writeLock();
		try {
			FileOutputStream f = new FileOutputStream(file, true);
			FileChannel ch = f.getChannel();
			ByteBuffer buffer = ByteBuffer.wrap(byteArray);
			ch.write(buffer, offset);
			f.close();
		} catch (Exception e) {
			fileDesc.writeUnlock();
			xdrReply = successAnswer(xdrPacket, 0, xid, NFSConst.NFSERR_IO);
			xdrReply.send();
			return;
		}
		fileDesc.writeUnlock();
		xdrReply = successAnswer(xdrPacket, NFSConst.FATTR_SIZE, xid,
				NFSConst.NFS_OK);
		addAttribute(xdrReply, fileDesc, handle);
		xdrReply.send();
	}

	private void setAttr(XDR xdrPacket, long xid) {
		FileHandle handle = FileHandle.readFromPacket(xdrPacket);
		XDR xdrReply = null;
		if (handle == null) {
			voidError(xdrPacket, xid, RPCConst.GARBAGE_ARGS);
			return;
		}

		FileDescriptor fileDesc = server.getFile(handle);
		File file = fileDesc.getFile();
		int mode = xdrPacket.getInt();

		xdrPacket.getUInt();
		xdrPacket.getUInt();
		xdrPacket.getUInt();

		xdrPacket.getUInt();
		xdrPacket.getUInt();

		long sec = xdrPacket.getUInt();
		long usec = xdrPacket.getUInt();

		if (usec == -1) {
			voidError(xdrPacket, xid, RPCConst.GARBAGE_ARGS);
			return;
		}

		boolean rperm = (mode & NFSConst.PERM_OR) == NFSConst.PERM_OR;
		boolean wperm = (mode & NFSConst.PERM_OW) == NFSConst.PERM_OW;
		boolean eperm = (mode & NFSConst.PERM_OE) == NFSConst.PERM_OE;

		file.setReadable(rperm);
		file.setWritable(wperm);
		file.setExecutable(eperm);

		long mtime = sec * 1000 + usec / 1000;
		file.setLastModified(mtime);
		xdrReply = successAnswer(xdrPacket, NFSConst.FATTR_SIZE, xid,
				NFSConst.NFS_OK);
		addAttribute(xdrReply, fileDesc, handle);
		xdrReply.send();
	}

	private void procNull(XDR packet, long xid) {
		XDR xdrReply = successAnswer(packet, 0, xid, NFSConst.NFS_OK);
		xdrReply.send();
	}

	public void checkPacket(XDR xdrPacket, long xid, long process) {
		System.out.println(process);
		switch ((int) process) {
		case NFSConst.NFSPROC_NULL:
			procNull(xdrPacket, xid);
			break;
		case NFSConst.NFSPROC_GETATTR:
			getAttribute(xdrPacket, xid);
			break;
		case NFSConst.NFSPROC_STATFS:
			statFS(xdrPacket, xid);
			break;
		case NFSConst.NFSPROC_LOOKUP:
			lookup(xdrPacket, xid);
			break;
		case NFSConst.NFSPROC_READDIR:
			readDirectory(xdrPacket, xid);
			break;
		case NFSConst.NFSPROC_MKDIR:
			makeDirectory(xdrPacket, xid);
			break;
		case NFSConst.NFSPROC_RMDIR:
			removeDirectory(xdrPacket, xid);
			break;
		case NFSConst.NFSPROC_CREATE:
			create(xdrPacket, xid);
			break;
		case NFSConst.NFSPROC_REMOVE:
			nfsprocRemove(xdrPacket, xid);
			break;
		case NFSConst.NFSPROC_RENAME:
			rename(xdrPacket, xid);
			break;
		case NFSConst.NFSPROC_READ:
			read(xdrPacket, xid);
			break;
		case NFSConst.NFSPROC_SETATTR:
			setAttr(xdrPacket, xid);
			break;
		case NFSConst.NFSPROC_WRITE:
			write(xdrPacket, xid);
			break;
		default:
			// no NFS process available
			voidError(xdrPacket, xid, RPCConst.PROC_UNAVAIL);
			break;
		}
	}

	private static void attachTimestamp(XDR packet, long timestamp) {
		long sec = (long) (timestamp / 1000);
		long msec = 1000 * (timestamp - (sec * 1000));

		packet.addUInt(sec);
		packet.addUInt(msec);
	}

	public long getProg() {
		return 100003;
	}

	public long getVersion() {
		return 2;
	}
}
